整篇會分成以下幾個部分:
filter 這個 method 的全寫應該是 Array.prototype.filter,有興趣可以看 Day 2 的介紹,這邊會直接使用 filter() 作為替代。
Array method 有不少會使用到 callback function,如果尚不熟悉的話,可以看 Day 2 的介紹。
範例使用的 callback 都會使用箭頭函式做介紹,如果尚不熟悉的話可以參考 MDN 的介紹。
最後會透過分析 ECMAScript 來驗證是否有吻合,如果覺得 ECMAScript 有點艱澀難懂,我們在 Day 4 、Day 5 有介紹其相關術語可以幫助閱讀。
當你需要從陣列找 所有 符合條件的 element 的時候。
filter() 會回傳一個 陣列,包含 所有 符合條件的 element, 如果沒有則回傳一個 空陣列。
你同時可能會想到的其他方法:
find() - 找到第 1 個符合條件的 elementfindLast() - 從陣列結尾開始,找到第 1 個符合條件的 elementincludes() - 確認元素是否有在陣列內indexOf() - 回傳元素的索引值findIndex() - 回傳元素的索引值some() - 測試陣列的元素是否至少有一個符合條件filter((element, index, array) => {
	/* 測試條件 */
})
filter(callbackFn,thisArg)
filter(function(element, index, array) {
	/* 測試條件 */
}, thisArg)
filter() 的第 1 個參數為 callback, 第 2 個參數為選擇性 (optional) 的 thisArg。
callback這個 callback 又稱為 testing function 或 predicate ,顧名思義它會被拿來測試某些條件,再準確一點應該稱作斷言 (assert) ,因為它最後回傳的值會被強制轉換成布林值 (true 或 false)!
當這個 callback 被呼叫時會帶入 element、index、array 三個參數。
filter() 會按照陣列元素的順序依次 (升冪) 呼叫這個 callback,直到這個 callback 回傳 true 或當陣列元素已被遍歷完畢即停止,換句話說,如果這個陣列有 5 個元素,那這個 callback 至多會被呼叫 5 次。
element
陣列當前的元素 (element),callback 的第 1 個參數,為 filter() 當前遍歷到的元素,也就表示 element 會依陣列的順序動態變化。
index
陣列當前元素的索引值 (index),callback 的第 2 個參數,為 filter() 當前遍歷到的元素其索引值,也就表示 index 會依陣列的順序動態變化。
array
呼叫 filter() 的陣列本身 (被遍歷的陣列本身), callback 的第 3 個參數,不論 filter() 當下遍歷到哪個元素上, array 都會指向被遍歷的陣列本身,也就是呼叫 filter() 的陣列本身。
thisArg為 filter() 的第 2 個選擇性參數,它會被傳入 callback 並作為其 this 的值,否則就會是 undefined。
請注意,如果 callback 使用箭頭函式的話則沒有作用!
可以參考 Example - 4 的寫法。
回傳一個新陣列,裡面包括所有符合條件的元素,也就是原陣列中符合 testing function 的所有元素 (回傳 true)。
如果沒有符合條件的元素則回傳空陣列。
不會變動到原陣列。
const numbers = [2, 3, 10, 12, 9, 4, 7]
const odds = numbers.filter(number => number % 2 !== 0)
console.log(odds)
// [3, 9, 7]
numbers
numbers 呼叫 filter() 時會順著原型鏈拿到放在 Array.prototype 指向的 prototype 物件上的 filter() methodfilter() 呼叫時需傳入了一個 callback (testing function),因此我們直接在 filter() 的參數裡定義一個帶有 number 參數的箭頭函式filter() 被呼叫時, filter() 會執行 callback,並帶入 element、index、array 等 3 個參數,其中的 element 會被指派給 number 這個參數odds
index 參數:const names = ['Tyler', 'Coby', 'Jason', 'Amber', 'Emma', 'Arpad', 'Alexsander']
const pickNames = names.filter((name, index) => name[0].toLowerCase() === 'a' && index <= 5)
console.log(pickNames)
// ['Amber', 'Arpad']
index 參數, callback 必須依照順序定義參數,不能略過前面的參數const foods = [
	{ name: 'kiwi', price: 120, type: 'fruit'},
	{ name: 'oyster', price: 250, type: 'seafood' },
	{ name: 'milk', price: 180, type: 'beverage' },
	{ name: 'cherry', price: 300, type: 'fruit' },
	{ name: 'mango', price: 420, type: 'fruit' }
]
const cheapFruits = foods.filter((food, index) => food.price <= 300 && food.type === 'fruit')
console.log(cheapFruits);
// [{ name: 'milk', price: 180, type: 'beverage' }, { name: 'cherry', price: 300, type: 'fruit' }]
thisArg 參數const stars = ["Charlize", "Emma", "Jake", "Collin", "Brad"]
const ages = [35, 16, 20, 41, 12]
const childStars = stars.filter(function(_star, index) {
	return this[index] < 18;
}, ages)
console.log(childStars)
// ['Emma', 'Brad']
ages 被作為 filter() 的第 2 個參數傳入this 會指向 ages
請注意 callback 定義時的參數順序,依序應為 element、index、array,但不可隨意略過,假如你只想使用 index 而不需要 element, 應該要照上面 Example 2 的寫法。
參數名稱可以隨意命名但通常會有一些慣例以增加可讀性,例如上面的例子便使用 number 來表示 numbers 這個陣列的每個元素。
有一點值得注意的是,雖然 filter() 不會變動到原陣列,但我們傳進去的 callback 卻有可能 ,而陣列元素被遍歷的範圍在第一次呼叫 callback 前就已經確立好了 (也就是 filter() 被呼叫後但 callback 尚未被呼叫),因此有可能會發生以下的狀況:
undefined
上述這種高併發 (concurrent) 的更動會導致程式碼非常難以閱讀,非常不建議使用 (除非有特殊的情境)。
Array.prototype.filter(callbackfn[,thisArg])

this 轉型成一個 object 後指派給 O
O 的長度並指派給 len
callbackfn 是不可呼叫的 object 則丟出一個 TypeErrorO 作為原型創造出一個長度為 0 的陣列並指派給 A
k
to
k < len 時,重複以下步驟PkO  是否有 Pk 這個屬性並將結果的布林值指派給 kPresentkPresent 為 true,則進行以下步驟O 的 Pk 屬性的值,並指派給 kValuecallbackfn 並帶入 « kValue, ?(k), O » 這個 argumentList,並將回傳的值轉成布林值後指派給 selectedselected 為 true ,則執行以下步驟to 轉型成字串,在 A 上新增一個值為 kValue 的 to 屬性to = to + 1k = k + 1A
演算法的前 3 個步驟都是用來做一些前置處理,包括轉型、確認長度、確認參數是否為一個 function 等...
步驟 4 則創造了一個類陣列,之後會被當作結果回傳
步驟 7 開始執行迴圈,最大圈數為陣列長度,計數從 0 開始,重複以下步驟:
callbackfn 並帶入被取出的 元素、 當前計數、 陣列本身 這 3 個參數出現 ? 的地方代表有可能會丟出錯誤,所以整個演算法有 7 處有機會丟出錯誤,例如步驟 1 的 ToObject(),當你傳入 Undefined 或 Null 即會丟出一個 TypeError 的錯誤
如果出現 ! ,則代表這個 abstract operation 絕對不會丟出錯誤,例如步驟 7 的 ToString() 它會在參數是一個 Symbol 時丟出一個 TypeError,但我們確定丟進去的是一個 Number (F(k)),因此不會有丟出錯誤的可能
ECMAScript 其實並沒有規定使用 filter() 的物件必須是一個陣列,從步驟 1 跟 Note 2 可以看得出來,我們來驗證一下:

filter() 是一個很常用的咩色,過濾資料很方便,你只需要將邏輯專注在這個 testing funciton 上並注意使用的參數即可。
thisArg 雖然挺方便的,但它在閱讀時有些不直覺,容易搞不清楚 callback 的 this 是指誰。
最後,希望大家可以開心地使用各種咩色,體驗它帶給你的便利,祝大家歸剛沒煩惱。
Array.prototype.find - MDN
Draft ECMA 262 - TC39
看到 example 3 想說有需要這樣強調嗎
...
=> food.price <=
...
結果看錯XD
我剛看也嚇了一跳!!!